package jp.ameba.meshistock.app.service;

import java.util.Iterator;
import java.util.List;

import jp.ameba.meshistock.app.meta.UploadedDataFragmentMeta;
import jp.ameba.meshistock.app.meta.UploadedDataMeta;
import jp.ameba.meshistock.app.model.UploadedData;
import jp.ameba.meshistock.app.model.UploadedDataFragment;

import org.slim3.controller.upload.FileItem;
import org.slim3.datastore.Datastore;
import org.slim3.util.ByteUtil;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Transaction;
import com.google.common.collect.Lists;


public class UploadService {

    /** 1つのデータの最大値は1MなのでFragmentのサイズを900000に設定 */
    private static final int FRAGMENT_SIZE = 900000;

    /** DataとDataFragmentそれぞれのエンティティへのマッピングクラスを取得 */
    private UploadedDataMeta d = UploadedDataMeta.get();
    private UploadedDataFragmentMeta f = UploadedDataFragmentMeta.get();

    /**
     * DataをDataFragmentに分割しながらアップロード
     *
     * @param formFile
     * @return
     */
    public UploadedData upload(FileItem formFile) {

        if (formFile == null)
            return null;

        List<Object> models = Lists.newArrayList();
        UploadedData data = new UploadedData();

        // datastoreに保存するデータの
        models.add(data);
        // 各種Dataのパラメータ指定
        data.setKey(Datastore.allocateId(d));
        data.setFileName(formFile.getShortFileName());
        data.setLength(formFile.getData().length);

//        InputStream is = new ByteArrayInputStream(formFile.getData());

        // formFileをバイト化
        byte[] bytes = formFile.getData();
        // bytesをFragmentのサイズに分割
        byte[][] bytesArray = ByteUtil.split(bytes, FRAGMENT_SIZE);

        // byteArrayの長さ分のKeyを生成
        Iterator<Key> keys =
            Datastore
                .allocateIds(data.getKey(), f, bytesArray.length)
                .iterator();
        for (int i = 0; i < bytesArray.length; i++) {
            // FragmentData用のbyte[]
            byte[] fragmentData = bytesArray[i];
            // インスタンス生成しmodelsに格納
            UploadedDataFragment fragment = new UploadedDataFragment();
            models.add(fragment);
            fragment.setKey(keys.next());
            fragment.setBytes(fragmentData);
            fragment.setIndex(i);
            fragment.getUploadDataRef().setModel(data);
        }

        Transaction tx = Datastore.beginTransaction();
        for (Object model : models)
            Datastore.put(tx, model);

        tx.commit();
        return data;
    }

    /**
     * Dataの情報を取得
     *
     * @param key
     * @param version
     * @return
     */
    public UploadedData getData(Key key, Long version) {
        return Datastore.get(d, key, version);
    }

    /**
     * 取得したDataの情報からFragmentをかき集めて修復
     *
     * @param uploadedData
     * @return
     */
    public byte[] getBytes(UploadedData uploadedData) {
        // Dataが空だった場合にNullPointExceptionを投げる
        if (uploadedData == null) {
            throw new NullPointerException(
                "The uploadedData parameter must not be null.");
        }
        // DataからFragmentの一覧を取得
        List<UploadedDataFragment> fragmentList =
            uploadedData.getFragmentListRef().getModelList();
        // 各々のFragmentからbyteを取得してbyte[][]に格納
        byte[][] bytesArray = new byte[fragmentList.size()][0];
        for (int i = 0; i < fragmentList.size(); i++) {
            bytesArray[i] = fragmentList.get(i).getBytes();
        }
        // bite[][]をjoinしてリターン
        return ByteUtil.join(bytesArray);
    }

    /**
     * 削除するDataのKeyを指定してDataとFragmentの両方を削除
     *
     * @param key
     */
    public void delete(Key key) {
        Transaction tx = Datastore.beginTransaction();
        // 削除するエンティティのKey一覧
        List<Key> keys = Lists.newArrayList();
        keys.add(key);
        keys.addAll(Datastore.query(f, key).asKeyList());
        // トランザクション内で削除とトランザクション完了
        Datastore.delete(tx, keys);
        Datastore.commit(tx);
    }
}
